﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Caliburn.Micro;

namespace Zero.Frontend
{
    /// <summary>
    /// An <see cref="IResult"/>, that takes a Type of ChildScreen, 
    /// locates/instanciates it (<see cref="_locateChild"/>), 
    /// locates its Parent (<see cref="In"/>, 
    /// configures the ChildScreen (<see cref="Configured"/>),
    /// inicializes the ChildScreen (<see cref="IAsyncInicializable"/>),
    /// and activates it in its Parent.
    /// </summary>
    /// <remarks>This class was taken from the Caliburn Framework</remarks>
    /// <typeparam name="TChild">The Child-Type</typeparam>
    public class OpenChildResult<TChild> : IResult
    {
        private Func<ActionExecutionContext, IConductor> _locateParent;
        private Action<TChild> _onConfigure;

        private readonly Func<ActionExecutionContext, TChild> _locateChild =
            c => IoC.Get<TChild>();

        private IConductor _parent;
        private TChild _child;

        /// <summary>
        /// Child instance is resolved through IoC.
        /// </summary>
        public OpenChildResult() { }

        /// <summary>
        /// Specifies child instance.
        /// </summary>
        /// <param name="child"></param>
        public OpenChildResult(TChild child)
        {
            _locateChild = c => child;
        }
        
        //public OpenChildResult<TChild> In<TParent>()
        //{
        //    _locateParent = c => IoC.Get<TParent>() as IConductor;
        //    return this;
        //}

        /// <summary>
        /// Shows child in specified conductor.
        /// </summary>
        /// <param name="parent"></param>
        /// <returns></returns>
        public OpenChildResult<TChild> In(object parent)
        {
            _locateParent = c => parent as IConductor;
            return this;
        }

        /// <summary>
        /// Configures child using lambda delegate.
        /// </summary>
        /// <param name="configure"></param>
        /// <returns></returns>
        public OpenChildResult<TChild> Configured(Action<TChild> configure)
        {
            _onConfigure = configure;
            return this;
        }

        void IResult.Execute(ActionExecutionContext context)
        {
            if (_locateParent == null)
                _locateParent = c => (IConductor)c.Target;

            _parent = _locateParent(context);
            _child = _locateChild(context);

            if (_onConfigure != null)
                _onConfigure(_child);

            IAsyncInicializable inicializable = _child as IAsyncInicializable;
            if (inicializable != null)
            {
                Coroutine.BeginExecute(inicializable.InicializeAsync().GetEnumerator(), context, InitCompleted);
            }
            else
            {
                ActivateChild();
            }

            
        }

        private void InitCompleted(object sender, ResultCompletionEventArgs e)
        {
            if (e.Error == null)
            {
                ActivateChild();
            }
            else
            {
                _completed(this, new ResultCompletionEventArgs() { Error = e.Error, WasCancelled = e.WasCancelled });
            }            
        }

        private void ActivateChild()
        {
            _parent.ActivateItem(_child);
            _completed(this, new ResultCompletionEventArgs());
        }

        private EventHandler<ResultCompletionEventArgs> _completed = delegate { };
        event EventHandler<ResultCompletionEventArgs> IResult.Completed
        {
            add { _completed += value; }
            remove { _completed -= value; }
        }
    }
}
